/*
 *    Copyright © OpenAtom Foundation.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package com.inspur.edp.bef.bizentity.util;


import com.inspur.edp.bef.bizentity.GspBizEntityElement;
import com.inspur.edp.bef.bizentity.GspBizEntityObject;
import com.inspur.edp.bef.bizentity.GspBusinessEntity;
import com.inspur.edp.cef.designtime.api.IGspCommonField;
import com.inspur.edp.cef.designtime.api.element.GspDeleteRuleType;
import com.inspur.edp.cef.designtime.api.element.GspElementObjectType;
import com.inspur.edp.lcm.metadata.api.entity.GspMetadata;
import com.inspur.edp.lcm.metadata.api.entity.GspProject;
import com.inspur.edp.lcm.metadata.api.entity.MetadataProject;
import com.inspur.edp.lcm.metadata.api.service.MetadataProjectService;
import com.inspur.edp.lcm.metadata.api.service.MetadataService;
import com.inspur.edp.lcm.metadata.api.service.RefCommonService;
import com.inspur.edp.svc.reference.check.api.IReferenceCfgManager;
import com.inspur.edp.svc.reference.check.api.ReferenceConfigInfo;
import com.inspur.edp.svc.reference.check.api.ReferenceType;
import com.inspur.edp.udt.designtime.api.entity.ComplexDataTypeDef;
import com.inspur.edp.udt.designtime.api.entity.SimpleDataTypeDef;
import com.inspur.edp.udt.designtime.api.entity.UnifiedDataTypeDef;
import com.inspur.edp.udt.designtime.api.entity.dbInfo.ColumnMapType;
import com.inspur.edp.udt.designtime.api.entity.element.UdtElement;
import io.iec.edp.caf.boot.context.CAFContext;
import io.iec.edp.caf.commons.utils.SpringBeanUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import lombok.var;

public class ReferenceService {

  private IReferenceCfgManager manager = SpringBeanUtils.getBean(IReferenceCfgManager.class);
  private static final String identifier = "/";
  private static final String SERVICEID = "Inspur.Gsp.Cef.Core.BEDeleteService.CheckReference";
  private java.util.ArrayList<String> ids = new java.util.ArrayList<String>();

  public String getServiceUnitCode() {
    return serviceUnitCode;
  }

  public void setServiceUnitCode(String serviceUnitCode) {
    this.serviceUnitCode = serviceUnitCode;
  }

  private String serviceUnitCode;

  public RefCommonService getRefCommonService() {
    return refCommonService;
  }

  public void setRefCommonService(RefCommonService refCommonService) {
    this.refCommonService = refCommonService;
  }

  private RefCommonService refCommonService;

  public ReferenceService() {

  }

  public static ReferenceService getInstance() {
    return new ReferenceService();
  }

  //注册引用
  public final void registerReference(GspMetadata metadata) {
    parseMetadata(metadata);
  }


  //解析be元数据
  private void parseMetadata(GspMetadata md) {
    GspBusinessEntity be = (GspBusinessEntity) ((md.getContent() instanceof GspBusinessEntity) ? md
        .getContent() : null);
    String su = getSU(md); //引用关联BE所属的SU
    String referre = be.getDotnetGeneratedConfigID(); //引用关联BE的BefConfig
    if (be == null) {
      return;
    }
    java.util.ArrayList<ReferenceConfigInfo> list = new java.util.ArrayList<ReferenceConfigInfo>();
    //遍历be各个节点
    for (var item : be.getAllObjectList()) {
      var obj = (GspBizEntityObject) item;
      var elements = obj.getAllElementList(true);
      //遍历节点的所有属性，并找出设置关联的属性字段
      for (var element : elements) {
        var field = (GspBizEntityElement) element;
        //TODO: 此方法暂时用不到, 多值udt上的关联将来考虑支持
        //DealUdt(field,su,referre,be.MainObject.getCode(),obj.getCode(),list);
        if (field.getObjectType() == GspElementObjectType.Association) {
          var associations = field.getChildAssociations();
          for (var assocaition : associations) {
            if (assocaition.getAssoModelInfo() == null || StringUtil
                .checkNull(assocaition.getAssoModelInfo().getModelConfigId())
                || assocaition.getDeleteRuleType() != GspDeleteRuleType.Refuse) {
              continue;
            }

            String befConfig = assocaition.getAssoModelInfo().getModelConfigId(); //关联的BE的BefConfig
            String mainCode = assocaition.getAssoModelInfo().getMainObjCode(); //关联的BE的主节点编号
            String nodeCode = assocaition.getRefObjectCode(); //关联的BE节点编号
            if (mainCode.equals(nodeCode)) {
              nodeCode = "RootNode";
            }
            String source = befConfig + identifier + nodeCode;
            // string su = getSU(md);//引用关联BE所属的SU
            ReferenceType referType = ReferenceType.BE;
            String refPropName = be.getDotnetGeneratedConfigID() +
                identifier + be.getMainObject().getCode() +
                identifier + obj.getCode() + identifier +
                field.getLabelID();
            //引用关联BE的字段
            ReferenceConfigInfo info = new ReferenceConfigInfo();
            info.setId(UUID.randomUUID().toString());
            info.setSource(source);
            info.setReferenceType(referType);
            info.setSu(su);
            info.setReferrer(referre);
            info.setRefPropName(refPropName);
            info.setServiceId(SERVICEID);
            info.setWarningMessage(
                String.format("要删除的数据已经被【%1$s】上的【%2$s】使用，无法删除！", item.getName(), field.getName()));
            //
            info.setCreator(CAFContext.current.getCurrentSession().getUserName());
            info.setCreatedTime(CAFContext.current.getCurrentDateTime());
            info.setLastModifiedTime(CAFContext.current.getCurrentDateTime());
            info.setLastModifier(CAFContext.current.getCurrentSession().getUserName());
            list.add(info);
          }

        }
      }
      java.util.HashMap<String, ReferenceConfigInfo> result = getDBRecord(su, referre);
      java.util.ArrayList<ReferenceConfigInfo> dbInfo = new java.util.ArrayList<ReferenceConfigInfo>();

      for (var value : result.values()) {
        dbInfo.add(value);
      }
      registerRefer(list, dbInfo);
    }
  }

  private String getSU(GspMetadata metadata) {
    if (serviceUnitCode == null) {
      MetadataProjectService service = SpringBeanUtils.getBean(MetadataProjectService.class);
      MetadataService metadataService = SpringBeanUtils.getBean(MetadataService.class);
      MetadataProject metadataInfo = service.getMetadataProjInfo(metadata.getRelativePath());
      GspProject projInfo = metadataService.getGspProjectInfo(metadataInfo.getProjectPath());
      serviceUnitCode = projInfo.getServiceUnitCode();
    }
    return serviceUnitCode;
  }


  //更新或注册元数据关联记录信息
  private void registerRefer(java.util.ArrayList<ReferenceConfigInfo> newInfo,
      java.util.ArrayList<ReferenceConfigInfo> dbInfo) {
    java.util.ArrayList<ReferenceConfigInfo> delete = new java.util.ArrayList<ReferenceConfigInfo>();
    java.util.ArrayList<ReferenceConfigInfo> update = new java.util.ArrayList<ReferenceConfigInfo>();
    java.util.ArrayList<ReferenceConfigInfo> insert = new java.util.ArrayList<ReferenceConfigInfo>();

    for (var item : dbInfo) {
      int flag = 0;

      for (var childItem : newInfo) {
        if (childItem.getRefPropName().equals(item.getRefPropName())) {
          if (!childItem.getSource().equals(item.getSource()) || !childItem.getWarningMessage()
              .equals(item.getWarningMessage())) {
            item.setSource(childItem.getSource());
            item.setWarningMessage(childItem.getWarningMessage());
            update.add(item);
          }
          break;
        }
        flag++;
      }
      if (flag == newInfo.size()) {
        delete.add(item);
      }

    }

    for (var item : newInfo) {
      int flag = 0;

      for (var childItem : dbInfo) {
        if (childItem.getRefPropName().equals(item.getRefPropName())) {
          break;
        }
        flag++;
      }
      if (flag == dbInfo.size()) {
        insert.add(item);
      }
    }

    for (var item : delete) {
      manager.delete(item.getId());
    }

    for (var item : insert) {
      manager.register(item);
    }

    for (var item : update) {
      manager.update(item);
    }
  }

  //获取元数据的历史关联记录
  private java.util.HashMap<String, ReferenceConfigInfo> getDBRecord(String su, String referre) {
    java.util.HashMap<String, ReferenceConfigInfo> result = new java.util.HashMap<String, ReferenceConfigInfo>();
    //获取数据库be上已有关联记录
    List<ReferenceConfigInfo> dbRecord = manager.query(
        item -> item.getReferenceType() == ReferenceType.BE && item.getSu().equals(su) && item
            .getReferrer().equals(referre));
    if (dbRecord == null) {
      return result;
    }
    for (var record : dbRecord) {
      result.put(record.getId(), record);
    }
    return result;
  }


  //Udt的关联解析
  private void dealUdt(IGspCommonField element, String su, String referer, String main, String node,
      ArrayList<ReferenceConfigInfo> list) {
    if (element.getIsUdt()) {
      String udtId = element.getUdtID();
      GspMetadata md = getMetadata(udtId);
      UnifiedDataTypeDef udt = (UnifiedDataTypeDef) ((md.getContent() instanceof UnifiedDataTypeDef)
          ? md.getContent() : null);
      if (udt == null) {
        return;
      }
      if ((SimpleDataTypeDef) udt == null) {
        ComplexDataTypeDef cudt = (ComplexDataTypeDef) udt;
        if (cudt.getDbInfo().getMappingType() == ColumnMapType.MultiColumns) {

          var elements = cudt.getElements();

          for (var item : elements) {
            UdtElement udtElement = (UdtElement) item;
            dealUdt(item, su, referer, main, node, list);
            if (udtElement.getObjectType() == GspElementObjectType.Association) {

              var associations = udtElement.getChildAssociations();

              for (var assocaition : associations) {
                GspMetadata refMd = getMetadata(assocaition.getRefModelID());

                var beEntity = (GspBusinessEntity) (
                    (refMd.getContent() instanceof GspBusinessEntity) ? refMd.getContent() : null);
                String befConfig = beEntity.getDotnetGeneratedConfigID();
                String mainCode = beEntity.getMainObject().getCode();
                String nodeCode = assocaition.getRefObjectCode();
                if (mainCode.equals(nodeCode)) {
                  nodeCode = "RootNode";
                }
                String source = befConfig + identifier + nodeCode;
                ReferenceType referType = ReferenceType.BE;
                String refPropName = referer + identifier + main + identifier + node + identifier
                    + GetUdtPersistColuName(element, udtElement.getLabelID());
                ReferenceConfigInfo info = new ReferenceConfigInfo();
                info.setId(UUID.randomUUID().toString());
                info.setSource(source);
                info.setReferenceType(referType);
                info.setSu(su);
                info.setReferrer(referer);
                info.setRefPropName(refPropName);
                info.setServiceId(SERVICEID);
                //
                info.setCreator(CAFContext.current.getCurrentSession().getUserName());
                info.setCreatedTime(CAFContext.current.getCurrentDateTime());
                info.setLastModifiedTime(CAFContext.current.getCurrentDateTime());
                info.setLastModifier(CAFContext.current.getCurrentSession().getUserName());
                list.add(info);
              }
            }
          }
        }
      } else {
        SimpleDataTypeDef sudt = (SimpleDataTypeDef) udt;
        if (sudt.getObjectType() == GspElementObjectType.Association) {

          var associations = sudt.getChildAssociations();

          for (var assocaition : associations) {
            GspMetadata refMd = getMetadata(assocaition.getRefModelID());

            var beEntity = (GspBusinessEntity) ((refMd.getContent() instanceof GspBusinessEntity)
                ? refMd.getContent() : null);
            String befConfig = beEntity.getDotnetGeneratedConfigID();
            String mainCode = beEntity.getMainObject().getCode();
            String nodeCode = assocaition.getRefObjectCode();
            if (mainCode.equals(nodeCode)) {
              nodeCode = "RootNode";
            }
            String source = befConfig + identifier + nodeCode;
            ReferenceType referType = ReferenceType.BE;
            String refPropName =
                referer + identifier + main + identifier + node + identifier + element.getCode();
            ReferenceConfigInfo tempVar2 = new ReferenceConfigInfo();
            tempVar2.setId(UUID.randomUUID().toString());
            tempVar2.setSource(source);
            tempVar2.setReferenceType(referType);
            tempVar2.setSu(su);
            tempVar2.setReferrer(referer);
            tempVar2.setRefPropName(refPropName);
            tempVar2.setServiceId(SERVICEID);
            ReferenceConfigInfo info = tempVar2;
            list.add(info);
          }
        }
      }

    }
  }

  private GspMetadata getMetadata(String metadataId) {
    if (refCommonService == null) {
      return SpringBeanUtils.getBean(RefCommonService.class).getRefMetadata(metadataId);
    }
    return refCommonService.getRefMetadata(metadataId);
  }


  private String GetUdtPersistColuName(IGspCommonField field, String code) {
    return field.getLabelID() + "_" + code;
  }


}

